export class XcUploadTool {
  public static renewalSize = 10485760;
  public config = {
    appId: '', // AppId
    sign: '', // 签名信息
    token: '', // 用户token
    uploadType: 'token', // 上传类型，token上传/sign上传
    uploadUrl: '', // 上传的url地址
    randomStr: '', // 随机字符串
    timeStamp: '', // 时间戳
    isHttp: true, // 是否发送http请求
    dataArray: [{
      id: '',
      file: null, // 文件
      folderPath: '', // 目录路径
      resumed: true, // 开启断点续传
      folderId: '', // 目录id
      // 一下是内部使用不需要用户传入
      reader: null, // 读取文件对象
      current: 0, // 当前值
      md5: '', // md5值
      uploadUrl: '', // 上传的url地址
      stopUpload: false // 暂停上传
    }],
    verifyStop: (data, reader, that) => {
      // 验证是否暂停
      // data：传入的数据对象
      // reader：加载对象为空表示上传暂停
      // that: this对象
      return false;
    },
    loadingStart: (current, data, that) => {
      // 开始加载（计算md5）
      // current：当前进度
      // data：传入的数据对象
      // that: this对象
    },
    loadingProgress: (current, data, that) => {
      // 加载（计算md5）进度
      // current：当前进度
      // data：传入的数据对象
      // that: this对象
    },
    loadingSuccess: (md5, current, data, that) => {
      // 加载（计算md5）完成
      // md5：计算出的md5
      // data：传入的数据对象
      // current：当前进度
      // that: this对象
    },
    uploadStart: (current, data, that) => {
      // 开始上传
      // current：当前进度
      // data：传入的数据对象
      // that: this对象
    },
    uploadProgress: (current, data, that) => {
      // 上传进度
      // current：当前进度
      // data：传入的数据对象
      // that: this对象
    },
    uploadSuccess: (current, data, res, that) => {
      // 上传完成
      // current：当前进度
      // data：传入的数据对象
      // res：请求返回对象
      // that: this对象
    },
    uploadFail: (data, res, that) => {
      // 上传失败
      // current：当前进度
      // data：传入的数据对象
      // res：请求返回对象
      // that: this对象
    }
  };

  /**
   * 获取数据对象
   */
  public static getData(id, dataArray) {
    for (const arr of dataArray) {
      if (arr.id === id) {
        return arr;
      }
    }
  }

  /**
   * 上传
   */
  public upload(config) {
    if (config) {
      this.config = config;
    } else {
      config = this.config;
    }
    const myThis = this;
    for (const data of config.dataArray) {
      myThis.getMd5(data, config, (md5) => {
        myThis.uploadFile(data, config, md5, config.uploadUrl, null, null);
      });
    }
  }

  /**
   * 获取文件hash值
   */
  public getMd5(data, config, fun) {
    let current = 0;
    if (config.loadingStart) {
      config.loadingStart(current, data, this);
    }
    const file = data.file;
    const reader = new FileReader();
    // @ts-ignore
    const SparkMD5 = require('spark-md5');
    const spark = new SparkMD5.ArrayBuffer();
    reader.readAsArrayBuffer(file.slice(current, current + XcUploadTool.renewalSize));
    reader.onload = (e) => {
      if (config.verifyStop ? config.verifyStop(data, reader, this) : data.stopUpload) {
        data.reader = reader;
        data.current = current;
        return false;
      }
      spark.append(e.target['result']);
      if (file.size <= current) {
        const md5 = spark.end();
        if (config.loadingSuccess) {
          config.loadingSuccess(md5, current, data, this);
        }
        data.md5 = md5;
        fun(md5);
      } else {
        current += XcUploadTool.renewalSize;
        if (file.size <= current) {
          current = file.size;
        }
        reader.readAsArrayBuffer(file.slice(current, current + XcUploadTool.renewalSize));
        if (config.loadingProgress) {
          config.loadingProgress(current, data, this);
        }
      }
    };
  }

  /**
   * 上传文件
   */
  public uploadFile(data, config, md5, uploadUrl, current, renewalSize) {
    if (!renewalSize) {
      renewalSize = 0;
    }
    if (!current) {
      current = 0;
    }
    const that = this;
    if (config.uploadStart) {
      config.uploadStart(current, data, this);
    }
    if (!config.isHttp) {
      return;
    }
    const file = data.file;
    // 上传文件
    const formData = new FormData();
    const blob = file.slice(current, current + renewalSize);
    formData.append('file', blob);
    formData.append('name', file.name);
    formData.append('hashCode', md5);
    formData.append('fileIndex', current);
    formData.append('size', file.size);
    // 签名上传
    if (config.uploadType === 'sign') {
      formData.append('appId', config.appId);
      formData.append('sign', config.sign);
      formData.append('randomStr', config.randomStr);
      formData.append('timeStamp', config.timeStamp);
    } else {
      formData.append('folderId', data.folderId);
    }
    // 目录处理
    formData.append('folderPath', data.folderPath);
    const httpRequest = new XMLHttpRequest();
    httpRequest.open('POST', uploadUrl);
    if (config.uploadType === 'token') {
      httpRequest.setRequestHeader('token', config.token);
    }
    httpRequest.upload.addEventListener('progress', (evt) => {
      if (!data.resumed) {
        if (evt.loaded === evt.total) {
          if (config.uploadSuccess) {
            config.uploadSuccess(evt.loaded, data, null, that);
          }
        } else {
          if (config.uploadProgress) {
            config.uploadProgress(evt.loaded, data, that);
          }
        }
      }
    }, false);
    httpRequest.onloadend = (e) => {
      if (config.verifyStop ? config.verifyStop(data, null, this) : data.stopUpload) {
        data.reader = null;
        return false;
      }
      if (e.target['status'] === 200) {
        if (e.target['response']) {
          const res = JSON.parse(e.target['response']);
          if (res.finish) {
            if (config.uploadSuccess) {
              config.uploadSuccess(res.fileIndex, data, res, that);
            }
          } else {
            if (config.uploadProgress) {
              config.uploadProgress(res.fileIndex, data, that);
            }
            data.uploadUrl = res['uploadUrl'];
            that.uploadFile(data, config, md5, res['uploadUrl'], res.fileIndex, res.renewalSize);
          }
        }
      } else if (e.target['status'] === 400) {
        if (config.uploadProgress) {
          config.uploadFail(data, e.target['response'], that);
        }
      } else {
        if (config.uploadProgress) {
          config.uploadFail(data, e.target['response'], that);
        }
      }
      return false;
    };
    httpRequest.send(formData);
  }

  /**
   * 重试
   */
  public retry(id) {
    const data = XcUploadTool.getData(id, this.config.dataArray);
    const uploadUrl = data.uploadUrl ? data.uploadUrl : this.config.uploadUrl;
    this.uploadFile(data, this.config, data.md5, uploadUrl, null, null);
  }

  /**
   * 开始
   */
  public start(id) {
    const data = XcUploadTool.getData(id, this.config.dataArray);
    data.stopUpload = false;
    if (data.reader) {
      data.reader.readAsArrayBuffer(data.file.slice(data.current, data.current + XcUploadTool.renewalSize));
    } else {
      const uploadUrl = data.uploadUrl ? data.uploadUrl : this.config.uploadUrl;
      this.uploadFile(data, this.config, data.md5, uploadUrl, null, null);
    }
  }

  /**
   * 暂停
   */
  public stop(id) {
    const data = XcUploadTool.getData(id, this.config.dataArray);
    data.stopUpload = true;
  }

  /**
   * 处理文件目录
   */
  public handleFileDirectory(dataTransfer) {
    const that = this;
    dataTransfer = dataTransfer || {};
    const items = dataTransfer.items;
    if (items && items.length && (items[0].webkitGetAsEntry || items[0].getAsEntry)) {
      const array = [];
      for (const item of items) {
        if (item.webkitGetAsEntry) {
          const entry = item.webkitGetAsEntry();
          if (entry) {
            entry._file = item.getAsFile();
          }
          array.push(entry);
        } else {
          array.push(item.getAsEntry());
        }
      }
      return new Promise((resolve, reject) => {
        that.handleDirectoryTree(array, [], resolve, reject);
      });
    }
    return new Promise((resolve, reject) => {
      const array = [];
      for (const file of dataTransfer.files) {
        array.push(file);
      }
      resolve(array);
    });
  }

  /**
   * 递归处理文件树
   */
  public handleDirectoryTree(entries, array, resolve, reject) {
    let bool = true;
    for (const entry of entries) {
      if (entry.isFile) {
        array.push(entry);
      } else {
        bool = false;
        const dirReader = entry.createReader();
        this.readEntries(dirReader, array, [], resolve, reject);
      }
    }
    if (bool) {
      const fileList = [];
      for (const entry of array) {
        if (entry._file) {
          Object.defineProperty(entry._file, 'webkitRelativePath', {
            value: entry['fullPath'],
            writable: false,
            enumerable: true,
            configurable: true
          });
          fileList.push(entry._file);
          if (array.length === fileList.length) {
            resolve(fileList);
          }
        } else {
          entry.file((file) => {
            Object.defineProperty(file, 'webkitRelativePath', {
              value: entry['fullPath'],
              writable: false,
              enumerable: true,
              configurable: true
            });
            fileList.push(file);
            if (array.length === fileList.length) {
              resolve(fileList);
            }
          }, (e) => {
            console.error(e);
            reject(e);
          });
        }
      }
    }
  }

  /**
   * 读取文件目录下的文件信息
   * 每次读取100条
   */
  public readEntries(dirReader, array, dirList, resolve, reject) {
    const that = this;
    dirReader.readEntries((results) => {
      if (!results.length) {
        that.handleDirectoryTree(dirList, array, resolve, reject);
      } else {
        dirList = dirList.concat(results);
        that.readEntries(dirReader, array, dirList, resolve, reject);
      }
    }, (e) => {
      console.error(e);
      reject(e);
    });
  }
}
